The programs in this directory illustrate a technique for drawing true shadows in real-time using the stencil planes of a VGX workstation. The technique is described in an article in the November-December, 1991 issue of IRIS Universe, the text of which is included here in the Showcase file "Article.showcase", and the figures in the SGI image files "fig1.rgb", etc. To view the article, execute "showcase Article.showcase". To see the figures, execute "ipaste filename". ------------------------------------------------------------------------------ Running the Program ------------------------------------------------------------------------------ If the executables "shadows", "bintoglo", and "glotopdat" are not present, type "make" to create them. "shadows" demonstrates the technique. When run without any arguments, it displays a scene of three cubes lit with a red and a blue point light source. If the workstation does not have stencil planes, a warning will appear, and it will be impossible to see stencil mask values or shadows, but the program will still run. Holding the middle mouse down lets the viewer rotate the scene trackball style. If the middle mouse button is released while the mouse is moving, the scene will continue to spin. The left mouse button translates the scene, and the left and middle buttons held down together track into and out of the scene with the vertical mouse motion. The right mouse button presents a pop-up menu of several display options. The initial mode is "Show Objects Only", in which no shadow calculations are done. "Show Volumes" displays the shadow volume faces projected away from one of the light sources as semi-transparent surfaces. This is helpful in understanding how treating these faces as displayed polygons lets the z-buffer do the work of shadow-object intersection. In this mode, The silhouette edges of each object are displayed as solid or dotted yellow lines. Silhouette edges are where the surface of an object changes from facing the light source to facing away from the light source. The work done in shadow calculation can be reduced if only those edges are used for the shadow calculation. "Show Mask" displays how the shadow face drawing accumulates to put values into the stencil planes - clear where no shadow volume faces cover the scene, magenta at the pixels where you go out of shadow exactly as many times as you go into it, blue at the pixels where you have gone into shadow once more than you have gone out, and blue-green where you have gone in two times more than you have gone out. In this display mode, the stencil value is accumulated differently, effectively counting the times gone it and the times gone out, not just the running total. As a result, parts of the mask may be displayed incorrectly or clear due to stencil overflow. The purpose of this display mode is to get a feel for what ends up in the stencil planes. "Show Shadows" displays the scene with shadows calculated. This is done by drawing the scene once using only the Ambient and Emitted parts for each object's material description. These are the components which would determine the color of the object at a pixel in shadow. Next, the stencil planes are cleared and each shadow volume face is drawn, incrementing or decrementing a mask in the stencil planes. Finally, the scene is drawn again using only the Diffuse and Specular components of each object's material description and the blendfunction set to (BF_ONE, BF_ONE) so that calculated colors are added to the colors in the frame buffer. Since all material components combine linearly, this gives the same result as if each object had been drawn once using all its components - Emitted, Ambient, Diffuse, and Specular. However, the drawing of the Diffuse and Specular parts is limited to those pixels where the stencil value is zero, indicating the visible surface there is not in shadow. "shadows" takes an optional command-line argument specifying a scene description file to use. Several examples are included in the directory "scenes". These scene files contain references to geometry and texture files in the "models" and "textures" directory. To be sure the correct files are found, always run "shadows" with the current directory set to the directory which contains the program, e.g. "./shadows scenes/balls" If the workstation has an accumulation buffer, the positions of the light sources can be jittered and several images accumulated to produce soft shadows. On workstations with an accumulation buffer, an additional menu item appears, "Turn Soft Shadows On/Off", allowing the jittering to be enabled or disabled. To see any effect, the lights in the scene must be defined as jitterable. The lights in the default three-cube scene are jitterable and will produce nice soft shadows. The default maximum number of frames to accumulate is 64, but intermediate results will be displayed, and the maximum value can be changed in a command-line option. Other command-line options allow setting a number of drawing parameters. These were designed primarily to allow for generation of specific images from a shell script. Type "shadows -h" to see all the options. ------------------------------------------------------------------------------ File Formats ------------------------------------------------------------------------------ The "shadows" program uses its own file format to describe 3D scenes and polygonal objects. By convention, scene description files are identified by a ".scn" suffix and 3D geometry files by a ".glo" suffix, and both are ASCII text format. Textures are stored in SGI image format with a ".rgb" suffix. Models readable by the demo program "flip", identified with a ".bin" suffix, can be converted into ".glo" format by the program "bintoglo". This is not a robust conversion process, but seems to work well in most cases. A command- line option, "-f", is provided to reverse the ordering of the vertices of ".bin" polygons, required in some cases to get front-facing and normal direction to work out. The ".bin" file format definitions and read/write routines are in "fastobj.h" and "fastobj.c". The "shadows" program does a significant amount of processing on the models to make the shadow face calculation easier, such as keeping edge-adjacency lists. When ".glo" files are read into the "shadows" program, all this processing must happen before any display can occur. The calculations are not particularly efficient and for some models this time was getting up around 30 seconds or so. To eliminate this problem, a binary format for the geometry files was created, including all the neat info the "shadows" program wants. These files are identified by a ".pdat" suffix. Routines for reading and manipulating these files are in "pdat.c". ".glo" files can be converted into ".pdat" files with the "glotopdat" utility. All scene information is contained in a ".scn" file. The first line of the file must be the single keyword "ShadowScene". Blank lines are ignored and newlines are treated as blank space. The near and far clipping planes, as well as the height of the viewing frustrum at the near clipping plane are specified by the WINDOW command. The LOOKAT command specifies look-from and look-to points. The MODEL command begins a new object definition and specifies the geometry file to read. This file may be either ".glo" or ".pdat" format. The XMODEL command is used to define an object which does not cast a shadow, useful for speeding up complex scenes. After the MODEL command come the optional tranformation, texture, and material specification. The POSITION, ROTATE, and SCALE commands may appear in any order, but only the last one of each takes effect, and the transformations are always done in the order Scale, Rotate, Position, with Rotation done in the order z, y, x. The equivalent gl sequence would be translate(...); rotate(..., 'x'); rotate(..., 'y'); rotate(..., 'z'); scale(...); Transformation commands which appear before any MODEL command are global scene transformations, done after individual object transformations. The TEXTURE command specifies the name of an SGI image format file to be used as a texture. Currently, all textures are mapped onto the x-z plane in object space using the texgen command. The mapping is specified by the arrays tex_sparams and tex_tparams in "shadows.c". It is an error for the TEXTURE command to appear before a MODEL or XMODEL command. The MATERIAL command is followed by a list of one or more specification commands - SPECULAR, SHININESS, DIFFUSE, EMISSION, or AMBIENT - followed by the command END. It is an error for the MATERIAL command to appear before a MODEL or XMODEL command. If no material is specified for an object, a default diffuse gray is assigned. The LIGHT command defines a new light source. It is followed by a list of light specification commands - AMBIENT, LCOLOR, POSITION, SPOTDIRECTION, SPOTLIGHT, JITTER - terminated by the END command. The commands follow the semantics of gl light definitions, and JITTER specified the dimensions of a cube within which the light is given a random position to simulate a non-point light source when jittering is enabled. If no lights are specified for a scene, a red one and a blue one are created. The light model used is defined by the constant theLModel in "shadow_scn.c". It specifies an ambient value of (0.2, 0.2, 0.2), with local viewer, attenuation, and two-sided lighting disabled. End-of-file or an END command finishes the scene definition. ------------------------------------------------------------------------------ Source Code Modules ------------------------------------------------------------------------------ The main event loop and display code is in "shadows.c". The main drawing routine is DrawFrame(), which uses a select statement to implement the proper drawing mode. The calculation of shadow volume faces is a fairly straightforward projection of the edges for a certain distance away from the light source. What complicates the technique is allowing the program to handle non-closed objects. When shared and non-shared edges appear in the same object, the into/outof counts can get confused. To avoid this, the program always draws its shadow volume faces as closed... The scene reading and setup is done by InitScene() in shadow_scn.c. It looks in the global variable sceneFileName to find the name of the ".scn" file to read. The scene file contains references to model and texture files, which are read on demand. InitScene() then fills in any missing scene description values, even a complete scene containing 3 cubes and 2 lights if no scene file is specified. The ".glo" 3D geometry data structure is defined, written, read, and displayed by defs and routines in "glo_obj.h" and "glo_obj.c". This is a simple format for 3D geometry which represents objects as a series of gl bgnpolygon/ v3f/endpolygon calls. In addition, lmbinds can be interspersed arbitrarily and objects can be saved without normals, with normal per face, or with normal per vertex. Currently only polygons are implemented, with hooks to include lines and tmeshes. The calculation of edge-adjacency lists and the reading of ".pdat" format files containing pre-computed information is done by routines in "pdat.c". The ".pdat" files are binary format and follow the variable formats defined in "shadows.h". "vect.h" and "vect.c" are useful simple routines for doing math on 3-element vectors and 4x4 matrices. These files are slight modifications of the files found in /usr/people/4Dgifts/examples/trackball. The files "trackball.h" and "trackball.c" are exact copies of files found in the same directory and contain the routines trackball() and build_rotmatrix() used to do quaternion based rotations. ------------------------------------------------------------------------------ Bugs ------------------------------------------------------------------------------ A bug in stencil operation in IRIX 3.x caused all stencil reference values to be truncated to 4 bits. Since the "shadows" program uses a value of 128 to represent a "zero" stencil value in order to allow its ingoing/outgoing count to go negative, things don't work too well. To get around this bug in 3.3 systems, change the constant STENZERO in "shadows.c" from 128 to 8. This will allow the program to work, though with not as great an in/out range as is possible with all 8 bits of current VGX hardware under IRIX 4.0. This change would also be necessary should SGI make a system with fewer than 8 stencil planes. A more robust approach would use getgdesc(GD_BITS_STENCIL) to pick a zero value. Stray pixels of the wrong shadow value can appear, especially in more complex images. I believe this is due to a shared edge of an into/outof shadow face pair not getting drawn the same way. Generally drawing shadow faces as a continuous strip of tmesh avoids any gap or overlap in pixel coverage, but stencil() commands are not allowed in the middle of a tmesh, so the mesh must be ended and begun again when switching between drawing into and outof shadow faces, allowing the possibility of errors. The program does not handle the special case of the viewpoint being in shadow. tph, 11/25/91
Source
Documentation
Images
Reference
Subdirectories